#ifndef TimeCounter_DL643A1_h
#define TimeCounter_DL643A1_h

extern "C" {
#include <sys/types.h>
#include "sis3100_vme_calls.h"
#include "sis3100_dma.h"
}

#include <string>
#include "stringutil.h"
#include "IOError.h"
#include "VmeModule.h"
#include "EventStruct.h"

class TimeCounter_DL643A1 : public VmeModule
{
 public:
  //read offsets, see manual
  const static unsigned int FIFO_OFFSET    = 0x00;
  const static unsigned int STATUS_OFFSET  = 0x04;
  const static unsigned int WORDS_OFFSET   = 0x08;

  //write offsets, see manual
  const static unsigned int RESET_OFFSET         = 0x00;
  const static unsigned int RESET_FIFOFF_OFFSET  = 0x04;
  const static unsigned int ENABLE_OUT_OFFSET    = 0x08;

  /// init constructor
  TimeCounter_DL643A1(int handle, unsigned int address)
    : VmeModule(handle,address) {}

  /// module reset, clears FIFO and all Flip-Flops
  void reset() throw(IOError)
  {
    int r;

    /// doesn't matther what you write to RESET_OFFSET, so I chose 0xaffe
    if( (r=vme_A16D32_write(handle,address+RESET_OFFSET,0xaffe))!=0 )
      throw IOError("TimeCounter_DL643A1::reset(): "
		    "vme_A16D16_write() returned non zero",r);

  }
  /// clears FIFO_FF (makes sense after an odd number of reads from 0x00)
  void resetFIFOFF() throw(IOError)
  {
    int r;

    /// doesn't matther what you write to RESET_OFFSET, so I chose 0xaffe
    if( (r=vme_A16D32_write(handle,address+RESET_FIFOFF_OFFSET,0xaffe))!=0 )
      throw IOError("TimeCounter_DL643A1::reset(): "
		    "vme_A16D16_write() returned non zero",r);
  }

  /// reads out one value from the fifo the "lame" A16D32 way
  u_int32_t readFIFO() throw(IOError)
  {
    u_int32_t hi,lo;
    int r;

    //according to the manual,
    //first the low and then the high word must be read!
    if( (r=vme_A16D32_read(handle,address+FIFO_OFFSET,&lo)) != 0 )
      throw IOError("TimeCounter_DL643A1::readFIFO():\n"
		    "  vme_A16D16_read() returned non zero",r);

    // read the high word
    if( (r=vme_A16D32_read(handle,address+FIFO_OFFSET,&hi)) != 0 )
      throw IOError("TimeCounter_DL643A1::readFIFO():\n"
		    "  vme_A16D16_read() returned non zero",r);

    /// check if FIFO-FULL-FLAG (=D14) or FIFO-EMPTY-FLAG (=D15) is set
    /// 1100 0000 0000 0000 = 0xc000
    if( (hi & 0x0000c000) != 0)
      throw
					IOError("TimeCounter_DL643A1::readFIFO():\n"
									"  FIFO-FULL-FLAG != 0 or FIFO-EMPTY-FLAG != 0",hi);

    /// mask out the flags 0011 1111 1111 1111 = 3fff
    hi &= 0x00ff;
    lo &= 0xffff;

    return ( (hi << 16) | lo );
  }

  static u_int32_t assemble( u_int32_t *buf )
  {
    return (((buf[0] & 0x00ff) << 16) | (buf[1] & 0xffff));
  }  

  /// reads fifo in dma32 mode
  /// hi and low words are put alternately in the buffer
  /// i.e.: lo,hi,lo,hi,lo,hi,lo,hi,lo,hi
  /// the two words must be assembled later
  /// use assemble(u_int32_t buf[2]) for this!
  /// count must be an even number (2 words for 1 event)
  /// for n events set count to 2*n!
  u_int32_t readFIFO32DMA( u_int32_t count, u_int32_t *buf ) throw(IOError)
  {
    int r;
    u_int32_t read;

    if( count % 2 )
      throw IOError("TimerCounter_DL643A1::readFIFO32DMA():\n"
		   " count must be an even number!",count);

    //read words
    if( (r=vme_A16DMA_D32FIFO_read(handle,address+FIFO_OFFSET,
				   buf,count,&read)) != 0 )
      throw IOError("TimeCounter_DL643A1::readFIFO32DMA():\n"
		    "  vme_A16DMA_D32FIFO_read() returned non zero\n",r);
    return read;
  }

  void ReadEvents( EventStruct *ev, u_int32_t count )
  {
			const static u_int32_t MAX_WORDS            = 1000;
      static u_int32_t buf[MAX_WORDS*2];

			if( count > MAX_WORDS*2 )
				throw IOError((string("TimeCounter_DL6431A1::ReadEvents(): requested ")
											 + count + " events - maximum is " + 2*MAX_WORDS).c_str(),0);
			
			if( readFIFO32DMA(2*count,buf) != 2*count )
				throw IOError("TimeCounter_DL6431A1::ReadEvents(): readFIFO32DMA()"
											" returned incorrect number of words"	,0);

			for(u_int32_t j=0; j<count; j++) {
					ev[j].tc = ((0x00ff & buf[2*j+1])<<16) | (0xffff & buf[2*j]);
			}
  }

  /// D15-D04=0, D03=Counter-Enable, D02=Counter-Overflow
  /// D01=FIFO-FULL D00=FIFO-EMPTY
  u_int16_t getStatus() throw(IOError)
  {
    u_int32_t d;
    int r;

    if( (r=vme_A16D32_read(handle,address+STATUS_OFFSET,&d)) != 0 )
      throw IOError("TimeCounter_DL643A1::getStatus():\n"
		    "  vme_A16D16_read() returned non zero",r);
    
    return (u_int16_t)d;
  }

  /// returns the number of words in the fifo
  /// 1 word = 1 event!
  /// fifo is 8096 words long, so u_int16_t is enough
  u_int16_t getWords()
  {
    u_int32_t d;
    int r;

    if( (r=vme_A16D32_read(handle,address+WORDS_OFFSET,&d)) != 0 )
      throw IOError("TimeCounter_DL643A1::getWords():\n"
		    "  vme_A16D32_read() returned non zero",r);
    
    return (u_int16_t)d;
  }

};

#endif
